/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.event.*;
import java.util.Vector;
import java.beans.*;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import org.openide.DialogDescriptor;
import org.openide.TopManager;
import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
/** Helper dialog box manager for showing custom property editors
* and indexed properties.
*
* @author Jan Jancura, Dafe Simonek
*/
public final class PropertyDialogManager {
/** Listener to editor property changes. */
private PropertyChangeListener listener;
/** Cache for reverting on cancel. */
private Object [] oldValues;
/** Custom property editor. */
private PropertyEditor editor;
/** Property displayer. */
private PropertyDisplayer displayer;
/** Synchronization lock. */
private Object lock;
/** Is property indexed? */
private boolean indexed = false;
/** Set true when property is changed. */
private boolean changed = false;
/** Given component stored for test on Enhance property ed. */
private Component component;
/** Dialog instance. */
private Dialog dialog;
// init ......................................................................
/** Create a dialog with the specified content, without
* any displayer, editor, or lock.
*
* @param title title of the dialog
* @param component component to show
* @param isModal <code>true</code> if the dialog should be modal
*/
public PropertyDialogManager (
final String title,
final Component component,
final boolean isModal
) {
this (title, component, isModal, null, null, null);
}
/** Create a dialog.
*
* @param title title of the dialog
* @param component component to show
* @param isModal <code>true</code> if the dialog should be modal
* @param displayer property displayer. If the editor is not <code>null</code>, then the
* displayer must be non-<code>null</code> as well.
* @param editor custom property editor. May be <code>null</code>.
* @param lock synchronization lock. May be <code>null</code>.
*/
public PropertyDialogManager (
final String title,
final Component component,
final boolean isModal,
final PropertyDisplayer displayer,
final PropertyEditor editor,
final Object lock
) {
this.displayer = displayer;
this.editor = editor;
this.component = component;
this.lock = lock;
if (lock == null) this.lock = new Object ();
// create dialog instance and initialize listeners
dialog = createDialog (title, component, isModal);
initializeListeners ();
}
// public methods ............................................................
/** Get the created dialog instance.
* @return the dialog instance managed by this class.
*/
public Dialog getDialog () {
return dialog;
}
// other methods ............................................................
/** Creates proper DialogDescriptor and obtain dialog instance
* via TopManager.createDialog() call.
*/
private Dialog createDialog (
final String title,
final Component component,
final boolean isModal
) {
// prepare our options (buttons)
Object defaultOption;
Object[] options;
if (editor == null) {
options = new Object[] {
PropertySheet.getString ("CTL_Close")
};
defaultOption = options[0];
} else {
boolean defaultValue = displayer.supportsDefaultValue ();
if (editor instanceof IndexedPropertyEditor) {
options = defaultValue ?
new Object[] {
PropertySheet.getString ("CTL_Default"),
PropertySheet.getString ("CTL_Close")
}
: new Object[] {
PropertySheet.getString ("CTL_Close")
};
defaultOption = options [0];
} else {
options = defaultValue ?
new Object[] {
PropertySheet.getString ("CTL_Default"),
PropertySheet.getString ("CTL_OK"),
PropertySheet.getString ("CTL_Cancel")
}
: new Object[] {
PropertySheet.getString ("CTL_OK"),
PropertySheet.getString ("CTL_Cancel")
};
defaultOption = options[0];
}
}
// create dialog descriptor, create & return the dialog
DialogDescriptor descriptor =
new DialogDescriptor(
component, title, isModal, options, defaultOption,
DialogDescriptor.DEFAULT_ALIGN, null,
new ActionListener () {
public void actionPerformed (ActionEvent evt) {
doButtonPressed (evt);
}
});
return TopManager.getDefault ().createDialog (descriptor);
}
/** Initializes dialog listeners. Must be called after
* createDialog method call. (dialog variable must not be null)
*/
private void initializeListeners () {
// dialog closing reactions
dialog.addWindowListener (new WindowAdapter () {
/** Ensure that values are reverted when user cancelles dialog
* by clicking on x image */
public void windowClosing (WindowEvent e) {
if (editor != null) cancelValue (); // not if property viewer only
// ensure that resources are released
dialog.setVisible (false);
dialog.dispose ();
}
/** Remove property listener on window close */
public void windowClosed (WindowEvent e) {
if (listener != null) editor.removePropertyChangeListener (listener);
}
});
// reactions to editor property changes
indexed = editor instanceof IndexedPropertyEditor;
if (editor != null) {
if (!indexed)
try {
oldValues = displayer.getPropertyDetails ().getPropertyValues ();
} catch (Exception e) {
// Ignored, there can be number of exceptions
// when asking for old values...
}
editor.addPropertyChangeListener (listener =
new PropertyChangeListener () {
/** Notify displayer about property change in editor */
public void propertyChange (PropertyChangeEvent e) {
Object o = null;
if (!indexed) o = PropertyDialogManager.this.editor.getValue ();
synchronized (lock) {
if (!indexed) {
changed = true;
PropertyDialogManager.this.displayer.setPropertyValue (o);
}
PropertyDialogManager.this.displayer.notifyPropertyChange (e);
}
}
}
);
}
}
/**
* Reverts to old values.
*/
private void cancelValue () {
if ( (!changed) ||
(component instanceof EnhancedCustomPropertyEditor)
) return;
synchronized (lock) {
if ((!indexed) && (oldValues != null)) {
try {
if ( displayer.getPropertyDetails ().getPropertyValues () !=
oldValues
) displayer.getPropertyDetails ().setPropertyValues (oldValues);
} catch (Exception e) {
// Ignored, there can be number of exceptions
// when asking for old values...
displayer.getPropertyDetails ().setPropertyValues (oldValues);
}
}
}
}
/** Called when user presses a button on some option (button) in the
* dialog.
* @param evt The button press event.
*/
private void doButtonPressed (ActionEvent evt) {
String label = evt.getActionCommand ();
if (label.equals (PropertySheet.getString ("CTL_Cancel")))
cancelValue ();
else
if (label.equals (PropertySheet.getString ("CTL_Default")))
displayer.restoreDefaultValue();
else
if ( label.equals (PropertySheet.getString ("CTL_OK")) &&
(component instanceof EnhancedCustomPropertyEditor)
) {
synchronized (lock) {
try {
if (!indexed)
displayer.setPropertyValue (
((EnhancedCustomPropertyEditor) component).getPropertyValue ()
);
displayer.notifyPropertyChange (
new PropertyChangeEvent (this, null, null, null)
);
} catch (IllegalStateException exc) {
// not a valid value
}
}
}
// close the dialog
changed = false;
dialog.setVisible (false);
dialog.dispose ();
}
}
/*
* Log
* 18 Gandalf 1.17 12/15/99 Jan Jancura
* 17 Gandalf 1.16 12/10/99 Jan Jancura Bug 1620
* 16 Gandalf 1.15 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 15 Gandalf 1.14 9/29/99 Ian Formanek Fixed bug 2825 - Closing
* Customizer using window close button throws an exception, Fixed Bug
* 4104 - A NullPointerException is thrown when custom property editor on
* read-only property is closed via the window's close button
* 14 Gandalf 1.13 9/15/99 Jaroslav Tulach More private things &
* support for default property.
* 13 Gandalf 1.12 7/8/99 Jesse Glick Removing all mention of
* context help from this class--the custom property editor itself should
* bind the context help, and NbDialog should pay attention.
* 12 Gandalf 1.11 7/8/99 Jesse Glick Bugfix relating to last
* change.
* 11 Gandalf 1.10 7/7/99 Jesse Glick Relying on
* DialogDescriptor's help support, rather than a separate button.
* 10 Gandalf 1.9 7/2/99 Jesse Glick Help button added to
* custom proped when it has a help context.
* 9 Gandalf 1.8 6/30/99 Ian Formanek reflecting changes of
* enhanced PropertyEditor interfaces
* 8 Gandalf 1.7 6/24/99 Jesse Glick Gosh-honest HelpID's.
* 7 Gandalf 1.6 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 6 Gandalf 1.5 4/4/99 Ian Formanek
* 5 Gandalf 1.4 3/20/99 Jaroslav Tulach DialogDescriptor has
* only ActionListener
* 4 Gandalf 1.3 3/20/99 Jesse Glick [JavaDoc]
* 3 Gandalf 1.2 3/18/99 Ian Formanek Added Help on
* PropertyDialog
* 2 Gandalf 1.1 3/18/99 Ian Formanek Changed buttons passed
* into DialogDescriptor from JButton to String
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
*/